from django.shortcuts import render, redirect
from django.views import View
from django import http
from django.contrib.auth import login, logout
import re
from .models import User, Address
from meiduo_mall.utils.response_code import RETCODE
from django_redis import get_redis_connection
from django.contrib.auth import authenticate
from django.contrib.auth.mixins import LoginRequiredMixin
import json
from celery_tasks.mail.tasks import send_user_email
from django.conf import settings
from meiduo_mall.utils import meiduo_signature
from . import contants


# Create your views here.

class RegisterView(View):
    def get(self, request):
        return render(request, 'register.html')

    def post(self, request):
        # 接收
        user_name = request.POST.get('user_name')
        pwd = request.POST.get('pwd')
        cpwd = request.POST.get('cpwd')
        phone = request.POST.get('phone')
        allow = request.POST.get('allow')
        sms_code_request = request.POST.get('msg_code')

        # 验证
        # 1-非空
        if not all([user_name, pwd, cpwd, phone, allow, sms_code_request]):
            return http.HttpResponseBadRequest('参数不完整')
        # 2-验证用户名-格式
        if not re.match('^[a-zA-Z0-9_-]{5,20}$', user_name):
            return http.HttpResponseBadRequest("请输入5-20个字符的用户名")
        # 验证用户是否存在
        if User.objects.filter(username=user_name).count() > 0:
            return http.HttpResponseBadRequest('用户已存在')
        # 验证密码
        if not re.match('^[0-9A-Za-z]{8,20}$', pwd):
            return http.HttpResponseBadRequest('请输入8-20位的密码')
        if pwd != cpwd:
            return http.HttpResponseBadRequest('两次密码不一致')
        # 手机号的验证
        if not re.match('^1[345789]\d{9}$', phone):
            return http.HttpResponseBadRequest('您输入的手机号格式不正确')
        # 手机号是否重复
        if User.objects.filter(mobile=phone).count() > 0:
            return http.HttpResponseBadRequest("手机号已存在")
        # 短信验证码
        # 连接redis  过期时间:从数据库获取connection设置的过期时间
        redis_cli = get_redis_connection('verify_code')

        # 读取短信验证码
        sms_code_redis = redis_cli.get('sms_' + phone)

        # 判断是否过期
        if not sms_code_redis:
            return http.HttpResponseBadRequest('短信验证码已过期')
        # 强制立即过期 验证码使用一次就过期
        redis_cli.delete('sms_' + phone)
        # 判断短信验证码是否正确
        if sms_code_redis.decode() != sms_code_request:
            return http.HttpResponseBadRequest('短信验证码错误')
        # 处理
        # 创建用户对象,保存到表中,先将密码加密,再保存到表中
        # user = User.objects.create(username=user_name,password=pwd,mobile=phone)
        user = User.objects.create_user(username=user_name, password=pwd, mobile=phone)

        # 状态保持
        # request.session['user_id']=user.id
        # 使用封装好的认证用户
        login(request, user)

        # 响应
        response = redirect('/')
        # user不可以直接写到这里.因为user接收的是手机号和用户名,现在需要提示用户名
        # 所以需要user.name来提取用户名,提示过期时间2周,cookie的过期时间是用户结束时
        response.set_cookie('username', user.username, max_age=60 * 60 * 24 * 14)
        return response


class UsernameCountView(View):  # 判断用户名是否存在
    def get(self, request, username):
        # 接收

        # 验证

        # 处理 查询判断 判断用户名是否存在,判断用户名的个数
        count = User.objects.filter(username=username).count()

        # 响应 如果是ajax请求需要返回json格式

        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'ok',
            'count': count
        })


class MobileCountView(View):  # 判断手机号是否存在
    def get(self, request, mobile):
        # 接收
        # 验证
        # 处理 查询统计
        count = User.objects.filter(mobile=mobile).count()
        # 响应
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'ok',
            'count': count
        })


class LoginView(View):
    def get(self, request):
        return render(request, 'login.html')

    def post(self, request):
        # 接收  使用post请求获取用户输入的账号密码,然后用变量接收
        username = request.POST.get('username')
        pwd = request.POST.get('pwd')

        # 接收重定向参数，可以使用返回指定页面或者返回登陆页面
        next_url = request.GET.get('next', '/')
        # 验证
        # 然后判断是用户名还是手机号登陆
        # 1-非空
        if not all([username, pwd]):
            return http.HttpResponseBadRequest('参数不完整')
        # 2-验证用户名-格式
        if not re.match('^[a-zA-Z0-9_-]{5,20}$', username):
            return http.HttpResponseBadRequest("请输入5-20个字符的用户名")
        # 验证用户是否存在
        # if User.objects.filter(username=username).count() > 0:
        #     return http.HttpResponseBadRequest('用户已存在')
        # 验证密码
        if not re.match('^[0-9A-Za-z]{8,20}$', pwd):
            return http.HttpResponseBadRequest('请输入8-20位的密码')

        # 处理  将上述的用户名和密码接收
        # 判断密码是否正确,是否登陆成功
        #  authenticate根据用户名查询对象,拿到对象进行密码判断
        # 将来用户输入的用户名可能是手机号也可能是用户名,需要使用user.去使用
        user = authenticate(username=username, password=pwd)
        # 用户名或密码错误
        # 响应
        if user is None:
            return render(request, 'login.html', {
                'loginerror': '用户名或密码错误'
                # loginerror是login.html里面42行.如果错误的话,返回错误提示信息
            })

        # 登陆成功 做状态保持功能
        else:
            # 返回user对象
            login(request, user)
            # 登陆成功后重定向到主页
            # 输出向cookie中输出用户名,用于在前端提示登陆状态
            # response = redirect('/')
            # 重复回到首页,如果用户登陆需要转回用户中心页面
            response = redirect(next_url)
            # user不可以直接写到这里.因为user接收的是手机号和用户名,现在需要提示用户名
            # 所以需要user.name来提取用户名,提示过期时间2周,cookie的过期时间是用户结束时
            response.set_cookie('username', user.username, max_age=60 * 60 * 24 * 14)
            return response


# 用户退出
class LogoutView(View):
    def get(self, request):
        # 删除session
        logout(request)
        # 删除cookie中的username
        response = redirect('/')
        response.delete_cookie('username')
        return response  # 用户退出后重定向至首页


# 进入用户中心页面/需要判断用户是否登陆,
class InfoView(LoginRequiredMixin, View):
    def get(self, request):  # 判断是否登陆.如果登陆显示界面
        #     if request.user.is_authenticated:    # is_authenticated用户对象的属性,判断是否登陆
        #         return render(request, 'user_center_info.html')
        #     else:  # 如果未登录,重定向至主页
        #         return redirect('/login/')

        # 获取当前登陆的用户
        user = request.user
        context = {
            'username': user.username,
            'mobile': user.mobile,
            'email': user.email,
            'email_active': user.email_active

        }

        # 使用了一个类 LoginRequiredMixin,封装了上面的方法及if语句
        # 配置LOGIN_URL,指定登陆视图的地址
        return render(request, 'user_center_info.html', context)


# 用户中心需要展示用户名，手机号邮箱信息
# 绑定邮箱
class EmailView(LoginRequiredMixin, View):
    def put(self, request):
        # 接收
        # 接收格式为字节，转换解码，使用json格式转换成字典
        # 以put方式发送的请求
        dict1 = json.loads(request.body.decode())
        email = dict1.get('email')

        # 验证：需要有email，判断
        if not all([email]):
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '没有邮箱参数',

            })
        # 验证邮箱的格式是否正确
        if not re.match('^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '邮箱格式错误',

            })

        # 处理
        # 修改属性
        user = request.user
        user.email = email
        user.save()
        # 保存

        # 发邮件
        token = meiduo_signature.dumps({'user_id': user.id}, contants.EMAIL_EXPIRES)
        verify_url = settings.EMAIL_VERIFY_URL + '?token=%s' % token
        send_user_email.delay(email, verify_url)

        # 响应EMAIL_VERIFY_URL
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'ok'
        })


# 激活邮箱地址
class EmailVerifyView(View):
    def get(self, request):
        # 接收
        token = request.GET.get('token')

        # 处理  需要解密
        dict1 = meiduo_signature.loadds(token, contants.EMAIL_EXPIRES)
        # 上面的值不一定会拿到，可能会被篡改，超过过期时间也不会拿到，所以需要做判断
        if dict1 is None:
            return http.HttpResponseBadRequest('激活信息无效，请重新发邮件')
        # 加密的时候，使用user_id,使用字典拿到用户的id
        user_id = dict1.get('user_id')
        # 验证  想要该谁，需要拿到用户对象
        try:  # pk==primary key 主键的意思
            user = User.objects.get(pk=user_id)
        except:  # 没查到就报个错
            return http.HttpResponseBadRequest('激活信息无效')
        else:  # 没出错的话执行
            user.email_active = True
            user.save()  # 保存，就修改了当前用户的值

        # 响应  重定向返回用户中心去，如果没登陆，就转至登录页
        return redirect('/info/')


# LoginRequiredMixin是显示登陆用户的信息，所以要继承用户登陆的视图/
class AddressView(LoginRequiredMixin, View):
    def get(self, request):
        # 查询当前用户的收货地址  如果获取一个对象用get，获取一个列表用filter
        user = request.user
        # user 指定用户，is_delete 逻辑删除   将其用字典方式返回至context，前端只识别json格式
        address_list = Address.objects.filter(user=user, is_delete=False)

        address_list2 = []
        for address in address_list:
            address_list2.append(address.to_dict())
            # 转换成json格式，前端拿到数据展示出来
        context = {
            'addresses': address_list2,  # 就是[{},{}]方括号里面一个一个的字典
            'user': user
        }

        return render(request, 'user_center_site.html', context)


# 增加地址  需要接收和返回的值   客户点击增加的时候，就会收到一个ajax的请求
# 将接收到的参数处理
class AddressCreateView(LoginRequiredMixin, View):
    def post(self, request):
        # 添加收货
        # 接收
        user = request.user
        # request.GET-接收查询参数的，地址里面有问号的
        # request.POST --<form method='post></form> 接收form表单
        # request.body--ajax请求中的json数据  json数据可以转换成字典
        dict1 = json.loads(request.body.decode())
        # title = dict1.get('title')
        receiver = dict1.get('receiver')
        province_id = dict1.get('province_id')
        city_id = dict1.get('city_id')
        district_id = dict1.get('district_id')
        place = dict1.get('place')
        mobile = dict1.get('mobile')
        tel = dict1.get('tel')
        email = dict1.get('email')

        # 验证
        # 非空验证  有值以后再做其他判断
        if not all([receiver, province_id, city_id, place, mobile]):
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '参数不完整'
            })
        # 对手机号的格式验证
        if not re.match(r'1[3-9]\d{9}$', mobile):
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '手机号格式错误'
            })
        # 电话的格式验证
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.JsonResponse({
                    'code': RETCODE.PARAMERR,
                    'errmsg': '固定电话格式错误'
                })
        # 邮箱验证
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.JsonResponse({
                    'code': RETCODE.PARAMERR,
                    'errmsg': '邮箱格式错误'

                })

                # 处理
                # 添加  创建对象
        address = Address.objects.create(
            user=user,
            title=receiver,
            province_id=province_id,
            city_id=city_id,
            district_id=district_id,
            detail=place,
            mobile=mobile,
            phone=tel,
            email=email,
        )
        # 判断当前用户是否有默认地址，如果没有则设置为当前地址
        if user.default_address is None:
            user.default_address = address
            user.save()

        # 响应
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'ok',
            'address': address.to_dict()

        })


# 修改收货地址，提交方式为put
class AddressUpdataView(LoginRequiredMixin, View):
    def put(self, request, address_id):
        # 接收和验证与注册的逻辑一样
        # 接收
        # ajax以json格式发过来的请求，使用request.body接收
        dict1 = json.loads(request.body.decode())
        # title = dict1.get('title')
        receiver = dict1.get('receiver')
        province_id = dict1.get('province_id')
        city_id = dict1.get('city_id')
        district_id = dict1.get('district_id')
        place = dict1.get('place')
        mobile = dict1.get('mobile')
        tel = dict1.get('tel')
        email = dict1.get('email')

        # 验证
        # 非空验证  有值以后再做其他判断
        if not all([receiver, province_id, city_id, place, mobile]):
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '参数不完整'
            })
        # 对手机号的格式验证
        if not re.match(r'1[3-9]\d{9}$', mobile):
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '手机号格式错误'
            })
        # 电话的格式验证
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.JsonResponse({
                    'code': RETCODE.PARAMERR,
                    'errmsg': '固定电话格式错误'
                })
        # 邮箱验证
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.JsonResponse({
                    'code': RETCODE.PARAMERR,
                    'errmsg': '邮箱格式错误'

                })

        # 处理:修改
        # 根据id查询用户对象
        user = request.user
        try:
            address = Address.objects.get(pk=address_id, user=user, is_delete=False)
        except:
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '收货地址编号错误'
            })
        address.receiver = receiver
        address.province_id = province_id
        address.city_id = city_id
        address.district_id = district_id
        address.detail = place
        address.mobile = mobile
        address.phone = tel
        address.email = email
        address.save()

        # 响应
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'ok',
            # 调用封装好的字典
            'address': address.to_dict()
        })

    def delete(self, request, address_id):
        # 查询对象，查询没删除的对象，并修改is_delete为Ture
        try:
            address = Address.objects.get(pk=address_id, user=request.user, is_delete=False)
        except:
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '收货地址编号无效'
            })
        # 将地址逻辑删除默认值改为Ture
        address.is_delete = True
        # 修改后保存
        address.save()
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'ok'
        })


# 设置默认地址，需要继承登陆类
class AddressDefaultView(LoginRequiredMixin, View):
    def put(self, request, address_id):
        user = request.user
        user.default_address_id = address_id
        user.save()

        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'ok'
        })


# 修改标题
class AddressTitleView(LoginRequiredMixin, View):
    def put(self, request, address_id):
        dict1 = json.loads(request.body.decode())
        title = dict1.get('title')

        if not all([title]):
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '收货地址标题不能为空'
            })

        try:
            address = Address.objects.get(pk=address_id, user=request.user, is_delete=False)
        except:
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '收货地址编号无效'
            })
        address.title = title
        address.save()
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'ok'
        })


# 修改密码 核对旧密码，修改新密码，需要核对两次的密码是否一致
class PasswordView(LoginRequiredMixin, View):
    def get(self, request):
        # 返回页面（前端做的页面）
        return render(request, 'user_center_pass.html')

    def post(self, request):
        # 接收
        # 接收旧密码和用户输入的两个新密码
        old_pwd = request.POST.get('old_pwd')
        new_pwd = request.POST.get('new_pwd')
        new_cpwd = request.POST.get('new_cpwd')

        # 验证

        # 验证非空
        if not all([old_pwd, new_cpwd, new_pwd]):
            return http.HttpResponseBadRequest('参数不完整')
        user = request.user
        # 验证旧密码是否正确
        if not user.check_password(old_pwd):
            return http.HttpResponseBadRequest('原密码错误')

        # 判断密码格式是否正确
        if not re.match('^[0-9A-Za-z]{8,20}$', new_pwd):
            return http.HttpResponseBadRequest('密码格式错误')
        # 两个新密码是否一致，做判断
        if new_pwd != new_cpwd:
            return http.HttpResponseBadRequest('密码不一致')
        # 处理 判断结束，修改密码，将密码保存
        user.set_password(new_pwd)
        user.save()
        # 响应  修改完毕后，将页面重新展示
        return render(request, 'user_center_pass.html')


